﻿// -----------------------------------------------------------------------
// <copyright file="Machinery.cs" company="DarkBone">
// DarkBone
// </copyright>
// -----------------------------------------------------------------------

namespace DarkBone.Engine.Networking
{
   using System;
   using System.Diagnostics;
   using System.IO;
   using System.Net.Sockets;

   /// <summary>
   /// Networking machinery
   /// </summary>
   public class Machinery
   {
      /// <summary>
      /// Receives the specified result.
      /// </summary>
      /// <param name="result">The result.</param>
      public static void OnReceive(IAsyncResult result)
      {
         Debug.Write(">> ");
         var message = (Message)result.AsyncState;
         if (!message.Socket.Connected)
         {
            return;
         }

         try
         {
            int read;
            try
            {
               read = message.Socket.EndReceive(result);
            }
            catch
            {
               // Connection dropped;
               message.Socket.Close();
               Debug.WriteLine("Connection dropped");
               return;
            }

            if (read == 0)
            {
               // Connection closed
               message.Socket.Close();
               Debug.WriteLine("Connection closed");
               return;
            }

            if (read > 0)
            {
               for (var i = 0; i < read; i++)
               {
                  if (message.LengthHeader.Count < 4)
                  {
                     message.LengthHeader.Add(message.Buffer[i]);
                     continue;
                  }

                  message.TransmissionBuffer.Add(message.Buffer[i]);
               }

               if ((/*(read == message.Buffer.Length) &&*/ (message.Length != message.TransmissionBuffer.Count)) || message.LengthHeader.Count < 4)
               {
                  Debug.WriteLine("more ({0}=={1}&&{2}!={3})||{4} [len<4]", read, message.Buffer.Length, message.Length, message.TransmissionBuffer.Count, message.LengthHeader.Count < 4);
                  message.Socket.BeginReceive(
                     message.Buffer, 0, message.Buffer.Length, SocketFlags.None, OnReceive, message);
               }
               else
               {
                  Debug.WriteLine("done1 {0}", message.TransmissionBuffer.Count);
                  message.Done(message);
               }
            }
            else
            {
               Debug.WriteLine("done2 {0}", message.TransmissionBuffer.Count);
               message.Done(message);
            }
         }
         catch (Exception ex)
         {
            Debug.WriteLine(ex);
         }
      }

      /// <summary>
      /// Ends sending the data, waits for a readline until the thread quits
      /// </summary>
      /// <param name="result">The result.</param>
      public static void OnSend(IAsyncResult result)
      {
         Debug.Write("<<");
         var message = (Message)result.AsyncState;
         var size = message.Socket.EndSend(result);
         message.Sent += size;
         if (message.Sent < message.Length)
         {
            Debug.WriteLine("Send succ " + message.Sent + " size=" + size + "lenght=" + message.Length);
            message.Socket.BeginSend(message.Buffer, message.Sent, message.Length - message.Sent, SocketFlags.None, Machinery.OnSend, message);
         }
         else
         {
            Debug.WriteLine("Sent {0}", size);
            message.Locker.Set();
         }
      }

      /// <summary>
      /// Reads content of the NetworkStream to MemoryStream
      /// </summary>
      /// <param name="clientStream">TCP Client Stream</param>
      /// <returns>
      /// Data read
      /// </returns>
      private static MemoryStream ReadContent(ref NetworkStream clientStream)
      {
         int readBytes;
         var readBytesTotal = 0;
         var contentLengthBuffer = new byte[4];
         var readBuffer = new byte[4096];
         do
         {
            readBytes = clientStream.Read(readBuffer, 0, 1);
            Array.Copy(readBuffer, 0, contentLengthBuffer, readBytesTotal, readBytes);
            readBytesTotal += readBytes;
         }
         while (readBytesTotal < 4);

         var contentLength = BitConverter.ToInt32(contentLengthBuffer, 0);
         readBytesTotal = 0;
         var memStream = new MemoryStream();
         do
         {
            readBytes = clientStream.Read(readBuffer, 0, readBuffer.Length);
            readBytesTotal += readBytes;
            memStream.Write(readBuffer, 0, readBytes);
         }
         while (readBytesTotal != contentLength);

         memStream.Position = 0;
         return memStream;
      }

      /// <summary>
      /// Writes content to stream
      /// </summary>
      /// <param name="clientStream">Stream to write to</param>
      /// <param name="content">Content to be written</param>
      private static void WriteContent(ref NetworkStream clientStream, Serializator.SerializedObject content)
      {
         clientStream.Write(BitConverter.GetBytes(content.Buffer.Length), 0, 4);
         clientStream.Flush();
         clientStream.Write(content.Buffer, 0, content.Buffer.Length);
         clientStream.Flush();
      }
   }
}
